using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using NUnit.Framework;

namespace Universe.Threading.Test
{
    [TestFixture]
    public class TestForEach_1
    {
        private readonly int[] Threads = new int[] { 1, 5, 10, 32, 64 };
        private const int Limit = 10007;

        [Test]
        public void Test_Exceptions()
        {
            foreach (int threads in Threads)
            {
                int[] probes = new int[] {0, 1, Limit/2 - 1, Limit/2, Limit - 2, Limit - 1};
                foreach (int probe in probes)
                {
                    // Trace.WriteLine(string.Format("Threads: {0}, Probe: {1}", threads, probe));
                    InvocationException<int> ex = null;
                    try
                    {
                        Parallel.ForEach(
                            RangeFor<Int32>.Forward(1, Limit),
                            threads, 
                            delegate(int index, int element)
                                {
                                    if (index == probe)
                                        throw new InvalidOperationException(
                                            "Failed to handle element " + element);
                                });
                    }
                    catch(InvocationException<int> e)
                    {
                        ex = e;
                    }

                    Assert.IsNotNull(ex, "InvocationException<int> expected at #" + probe);
                    Assert.AreEqual(ex.Index, probe, "Index of invalid element");
                    Assert.AreEqual(ex.Element, probe+1, "Value of invalid element");
                }
            }
        }
        
        [Test]
        public void Test_IndexesAndElements()
        {
                foreach (int threads in Threads)
                {
                    int[] elements = new int[Limit + 1];
                    int[] indexes = new int[Limit + 1];
                    Parallel.ForEach(RangeFor<Int32>.Forward(1, Limit), threads,
                        delegate(int index, int element)
                        {
                            elements[element] = 1;
                            indexes[index] = 1;
                        });

                    // test elements
                    {
                        int counter = 0;
                        int lostCount = 0;
                        for (int i = 1; i <= Limit; i++)
                            if (elements[i] == 0)
                                lostCount++;
                            else
                                counter++;

                        Trace.WriteLine(
                            string.Format(
                                "T{0} elements. Lost: [{1}], Counter: {2}",
                                threads,
                                lostCount,
                                counter));

                        string context = "Elements of " + threads + " threads";
                        Assert.AreEqual(Limit, counter, "Counter on " + context);
                        Assert.AreEqual(0, lostCount, "Lost on " + context);
                    }

                    // test indexes
                    {
                        int counter = 0;
                        int lostCount = 0;
                        for (int i = 0; i < Limit; i++)
                            if (indexes[i] == 0)
                                lostCount++;
                            else
                                counter++;

                        Trace.WriteLine(
                            string.Format(
                                "T{0} indexes. Lost: [{1}], Counter: {2}",
                                threads,
                                lostCount,
                                counter));

                        string context = "Indexes of " + threads + " threads";
                        Assert.AreEqual(Limit, counter, "Counter on " + context);
                        Assert.AreEqual(0, lostCount, "Lost on " + context);
                    }
                }
        }
    }
}
